#include "PageCache.h"

PageCache PageCache::_inst;

PageCache &PageCache::GetInst()
{
	return _inst;
}

Span *PageCache::NewSpan(size_t pageSize)
{
	assert(pageSize > 0 && pageSize < NPAGES); /*
	 std::cout << "向pageCache获取新的span, 页数是" << pageSize << std::endl;*/
	// DWORD tid = GetCurrentThreadId(); // 获取当前线程的线程标识符
	// std::thread::id tid = std::this_thread::get_id();
	// std::cout << "Current Thread ID: " << tid << std::endl; // 打印线程标识符

	// 1、如果当前的spanlist中含有，直接返回即可
	if (!_spanList[pageSize].Empty())
	{
		Span *newSpan = _spanList[pageSize].pop_front();

		for (size_t i = 0; i < newSpan->_pageCount; ++i)
		{
			// 给每一页都建立映射，方便后面进行合并
			_SiteToSpanHash[newSpan->_curPage + i] = newSpan;
		}
		// 下面两行代码是错误的，因为如果只映射首尾页，那么中间的页就无法进行合并了
		//_SiteToSpanHash[newSpan->_curPage] = newSpan;
		//_SiteToSpanHash[newSpan->_curPage + newSpan->_pageCount - 1] = newSpan;
		newSpan->_isUse = true;
		return newSpan;
	}

	// 2、如果当前的spanlist中没有了，冲下面的spanlist中去寻找看看是否存在对应的span可以拆分。
	for (size_t i = pageSize + 1; i < NPAGES; i++)
	{
		if (!_spanList[i].Empty())
		{
			Span *retSpan = _spanList[i].pop_front();

			// 处理返回链表的结点
			Span *newSpan = new Span;

			newSpan->_curPage = retSpan->_curPage + pageSize;
			newSpan->_pageCount = retSpan->_pageCount - pageSize;
			_spanList[i - pageSize].push_front(newSpan);

			// 处理hash表映射，后面好进行合并
			_SiteToSpanHash[newSpan->_curPage] = newSpan;
			_SiteToSpanHash[newSpan->_curPage + newSpan->_pageCount - 1] = newSpan;

			// 处理要返回的结点
			retSpan->_pageCount = pageSize;
			retSpan->_isUse = true;

			// hash表映射
			for (size_t j = 0; j < pageSize; j++)
			{
				_SiteToSpanHash[retSpan->_curPage + j] = retSpan;
			}
			return retSpan;
		}
	}

	// 3、如果都没有找到呢，直接去找系统内存。
	Span *span = new Span;
	void *memory = FetchMemoryFromSystem(NPAGES - 1);
	_recycle_page_to_system.insert({memory, NPAGES - 1});
	// 我们切内存分配好对应的spanlist是到centerCache中去切取，因此这里只要初始化页号和页的数量返回即可
	span->_curPage = (uintptr_t)memory >> PAGE_SHIFT;
	span->_pageCount = NPAGES - 1;
	_spanList[NPAGES - 1].push_front(span);
	return NewSpan(pageSize);
}

Span *PageCache::SiteToSpan(void *ptr)
{
	int pageNum = (uintptr_t)ptr >> PAGE_SHIFT;
	_lock.lock();
	auto ret = _SiteToSpanHash.find(pageNum);
	if (ret != _SiteToSpanHash.end())
	{
		Span *span = _SiteToSpanHash[pageNum];
		_lock.unlock();
		return span;
	}
	else
	{
		assert(false);
		_lock.unlock();
		return nullptr;
	}
}

void PageCache::RecycleToPageStorage(Span *span)
{

	// 一、向前合并的代码
	//  1、如果在哈希表中找不到前页的span，直接结束
	//  2、如果找到了，但是该span正在被使用，结束
	//  3、如果合并起来大于最大页数，那么结束
	while (true)
	{
		int prePage = span->_curPage - 1;
		if (_SiteToSpanHash.count(prePage))
		{
			Span *preSpan = _SiteToSpanHash[prePage];
			if (preSpan->_isUse)
				break;
			if (preSpan->_pageCount + span->_pageCount > NPAGES - 1)
				break;

			_spanList[preSpan->_pageCount].erase(preSpan);
			span->_curPage = preSpan->_curPage;
			span->_pageCount += preSpan->_pageCount;
			delete (preSpan);
		}
		else
		{
			break;
		}
	}

	// 二、向后合并思路相同。
	while (true)
	{
		int nextPage = span->_curPage + span->_pageCount;
		if (_SiteToSpanHash.count(nextPage))
		{
			Span *nextSpan = _SiteToSpanHash[nextPage];
			if (nextSpan->_isUse)
				break;
			if (nextSpan->_pageCount + span->_pageCount > NPAGES - 1)
				break;
			_spanList[nextSpan->_pageCount].erase(nextSpan);
			span->_pageCount += nextSpan->_pageCount;
			delete (nextSpan);
		}
		else
		{
			break;
		}
	}

	// 三、合并完成后记得将span放入hash表。
	_SiteToSpanHash[span->_curPage] = span;
	_SiteToSpanHash[span->_curPage + span->_pageCount - 1] = span;
	span->_isUse = false;
	_spanList[span->_pageCount].push_front(span);
	return;
}

void PageCache::release()
{
	for (auto &page : _recycle_page_to_system)
	{
		ReleaseMemoryToSystem(page.first, page.second);
	}
	for(size_t i = 0; i < NPAGES; i++)
	{
		while(!_spanList[i].Empty())
		{
			Span* span = _spanList[i].Begin();
			_spanList[i].pop_front();
			delete span;
		}
	}
}